/*
 * Copyright (C) 2000 - 2024 Silverpeas
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * As a special exception to the terms and conditions of version 3.0 of
 * the GPL, you may redistribute this Program in connection with Free/Libre
 * Open Source Software ("FLOSS") applications as described in Silverpeas's
 * FLOSS exception.  You should have received a copy of the text describing
 * the FLOSS exception, and it is also available here:
 * "https://www.silverpeas.org/legal/floss_exception.html"
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
package org.silverpeas.core.admin.component.model;

import org.silverpeas.core.BasicIdentifier;
import org.silverpeas.core.admin.persistence.ComponentInstanceRow;
import org.silverpeas.core.admin.service.OrganizationController;
import org.silverpeas.core.admin.space.model.SpacePath;
import org.silverpeas.core.admin.user.model.SilverpeasRole;
import org.silverpeas.core.admin.user.model.User;
import org.silverpeas.core.i18n.AbstractI18NBean;
import org.silverpeas.kernel.util.StringUtil;
import org.silverpeas.core.util.URLUtil;

import javax.persistence.Transient;
import java.util.Collection;
import java.util.Date;
import java.util.Optional;
import java.util.Set;

import static org.silverpeas.core.admin.user.model.SilverpeasRole.MANAGER;
import static org.silverpeas.kernel.util.StringUtil.isDefined;

/**
 * The class ComponentInstLight is the representation in memory of a component instance
 */
public class ComponentInstLight extends AbstractI18NBean<ComponentI18N>
    implements SilverpeasSharedComponentInstance {

  private static final long serialVersionUID = 4859368422448142768L;

  /* Unique identifier of the instance */
  private String id;

  /* Unique identifier of the father of the space */
  private String domainFatherId;

  /* instance Type */
  private String name;
  private Date createDate = null;
  private Date updateDate = null;
  private Date removeDate = null;
  private String status = null;
  private int createdBy = -1;
  private int updatedBy = -1;
  private int removedBy = -1;
  private int orderNum = -1;
  private String creatorName = null;
  private String updaterName = null;
  private String removerName = null;
  private boolean isInheritanceBlocked = false;
  private boolean hidden = false;
  private boolean publicApp = false;

  /** Used only in the aim to improve performances */
  @Transient
  private ComponentInst cachedComponentInst;

  @Override
  protected Class<ComponentI18N> getTranslationType() {
    return ComponentI18N.class;
  }

  /**
   * Constructor
   */
  public ComponentInstLight() {
    id = "";
    domainFatherId = "";
    name = "";
  }

  /**
   * Constructor
   */
  public ComponentInstLight(ComponentInstanceRow compo) {
    id = Integer.toString(compo.id);
    domainFatherId = Integer.toString(compo.spaceId);
    setLabel(compo.name);
    setDescription(compo.description);
    setLanguage(compo.lang);
    name = compo.componentName;

    if (compo.createTime != null) {
      createDate = new Date(Long.parseLong(compo.createTime));
    }
    if (compo.updateTime != null) {
      updateDate = new Date(Long.parseLong(compo.updateTime));
    }
    if (compo.removeTime != null) {
      removeDate = new Date(Long.parseLong(compo.removeTime));
    }
    status = compo.status;

    createdBy = compo.createdBy;
    updatedBy = compo.updatedBy;
    removedBy = compo.removedBy;

    orderNum = compo.orderNum;

    isInheritanceBlocked = compo.inheritanceBlocked == 1;
    hidden = compo.hidden == 1;
    publicApp = compo.publicAccess == 1;
  }

  @Override
  public BasicIdentifier getIdentifier() {
    return new BasicIdentifier(getLocalId(), getId());
  }

  @Override
  public String getId() {
    return name + id;
  }

  @Override
  public String getSpaceId() {
    return domainFatherId;
  }

  public int getLocalId() {
    return Integer.parseInt(id);
  }

  public void setLocalId(int id) {
    this.id = String.valueOf(id);
  }

  /**
   * Set the domain father id
   */
  public void setDomainFatherId(String sDomainFatherId) {
    this.domainFatherId = sDomainFatherId;
  }

  /**
   * Get the domain father id
   * @return the component instance father id. If the component instance has no father,
   * returns an empty string.
   */
  public String getDomainFatherId() {
    return getSpaceId();
  }

  /**
   * Has this component instance a domain father? In the case of a component instance, a domain
   * father is a space. For example, a component instance has no domain father when it belongs to
   * the user's personal space.
   * @return true if this component has a domain father, false otherwise.
   */
  public boolean hasDomainFather() {
    return StringUtil.isDefined(domainFatherId);
  }

  /**
   * Get the component name
   * This method is a hack (technical debt)
   * @return the component name
   */
  @Override
  public String getName() {
    return name;
  }

  /**
   * Get the component type
   * @return the component type
   */
  @Override
  public String getLabel() {
    return super.getName();
  }

  public void setLabel(String label) {
    super.setName(label);
  }

  @Override
  public Date getCreationDate() {
    return createDate;
  }

  public Date getRemovalDate() {
    return removeDate;
  }

  public String getStatus() {
    return status;
  }

  public void setStatus(String status) {
    this.status = status;
  }

  @Override
  public Date getLastUpdateDate() {
    return updateDate;
  }

  public String getCreatorName() {
    return creatorName;
  }

  public void setCreatorName(String creatorName) {
    this.creatorName = creatorName;
  }

  public String getRemoverName() {
    return removerName;
  }

  public void setRemoverName(String removerName) {
    this.removerName = removerName;
  }

  @SuppressWarnings("unused")
  public String getUpdaterName() {
    return updaterName;
  }

  @SuppressWarnings("unused")
  public void setUpdaterName(String updaterName) {
    this.updaterName = updaterName;
  }

  public int getCreatedBy() {
    return createdBy;
  }

  public int getRemovedBy() {
    return removedBy;
  }

  public int getUpdatedBy() {
    return updatedBy;
  }

  @Override
  public User getCreator() {
    return User.getById(String.valueOf(getCreatedBy()));
  }

  @Override
  public User getLastUpdater() {
    return User.getById(String.valueOf(getUpdatedBy()));
  }

  public String getPath(String separator) {
    SpacePath spacePath = SpacePath.getPath(domainFatherId);
    return spacePath.format(getLanguage(), true, separator);
  }

  /**
   * I18N
   */
  @Override
  public String getLabel(String language) {
    return super.getName(language);
  }

  /**
   * This method is a hack (technical debt)
   * @param name the name of the component.
   */
  @Override
  public void setName(String name) {
    this.name = name;
  }

  @Override
  public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + ((id == null) ? 0 : id.hashCode());
    return result;
  }

  public void setOrderNum(int orderNum) {
    this.orderNum = orderNum;
  }

  public int getOrderNum() {
    return orderNum;
  }

  @Override
  public int getOrderPosition() {
    return getOrderNum();
  }

  public boolean isInheritanceBlocked() {
    return isInheritanceBlocked;
  }

  public void setInheritanceBlocked(boolean isInheritanceBlocked) {
    this.isInheritanceBlocked = isInheritanceBlocked;
  }

  @Override
  public boolean isHidden() {
    return hidden;
  }

  @Override
  public boolean isPublic() {
    return publicApp;
  }

  @Override
  public boolean isWorkflow() {
    final Optional<WAComponent> component = WAComponent.getByName(getName());
    return component.isPresent() && component.get().isWorkflow();
  }

  @Override
  public boolean isTopicTracker() {
    final Optional<WAComponent> component = WAComponent.getByName(getName());
    return component.isPresent() && component.get().isTopicTracker();
  }

  public String getIcon(boolean bigOne) {
    return getIcon(this, bigOne);
  }

  public static String getIcon(final SilverpeasComponentInstance instance, final boolean bigOne) {
    final String app = instance.isWorkflow() ? "processManager" : instance.getName();
    final String size = bigOne ? "Big.png" : "Small.gif";
    return URLUtil.getApplicationURL() + "/util/icons/component/" + app + size;
  }

  @Override
  public Collection<SilverpeasRole> getSilverpeasRolesFor(final User user) {
    Set<SilverpeasRole> silverpeasRoles =
        SilverpeasRole.fromStrings(OrganizationController.get().getUserProfiles(user.getId(), getId()));
    silverpeasRoles.remove(MANAGER);
    return silverpeasRoles;
  }

  @Override
  public String getParameterValue(final String parameterName) {
    return getCachedComponentInst().getParameterValue(parameterName);
  }

  @Override
  public boolean isRemoved() {
    return ComponentInst.STATUS_REMOVED.equals(getStatus());
  }

  @Override
  public boolean equals(Object obj) {
    if (this == obj) {
      return true;
    }
    if (obj == null) {
      return false;
    }
    if (getClass() != obj.getClass()) {
      return false;
    }
    ComponentInstLight other = (ComponentInstLight) obj;
    if (id == null) {
      return other.id == null;
    } else return id.equals(other.id);
  }

  /**
   * Gets the linked {@link ComponentInst}.
   * @return the linked {@link ComponentInst}.
   */
  private ComponentInst getCachedComponentInst() {
    if (cachedComponentInst == null && isDefined(getId())) {
      cachedComponentInst = OrganizationController.get().getComponentInst(getId());
    }
    return cachedComponentInst;
  }
}
